<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>JavaKeeper</title>
    <meta name="generator" content="VuePress 1.5.4">
    <link rel="icon" href="/icon.svg">
    <script>
        var _hmt = _hmt || [];
        (function() {
            var hm = document.createElement("script");
            hm.src = "https://hm.baidu.com/hm.js?a949a9b30eb86ac0159e735ff8670c03";
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(hm, s);
            // 引入谷歌,不需要可删除这段
            var hm1 = document.createElement("script");
            hm1.src = "https://www.googletagmanager.com/gtag/js?id=UA-169923503-1";
            var s1 = document.getElementsByTagName("script")[0]; 
            s1.parentNode.insertBefore(hm1, s1);
        })();
        // 谷歌加载,不需要可删除
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-169923503-1');
    </script>
    <meta name="description" content="">
    <meta name="keywords" content="JavaKeeper,Java,Java开发,算法,blog">
    <link rel="preload" href="/assets/css/0.styles.91f57736.css" as="style"><link rel="preload" href="/assets/js/app.447d4224.js" as="script"><link rel="preload" href="/assets/js/3.9d76740c.js" as="script"><link rel="preload" href="/assets/js/1.c4fd7d2e.js" as="script"><link rel="preload" href="/assets/js/109.05f775e5.js" as="script"><link rel="prefetch" href="/assets/js/10.8cf3be2c.js"><link rel="prefetch" href="/assets/js/100.74f35ab8.js"><link rel="prefetch" href="/assets/js/101.7a062346.js"><link rel="prefetch" href="/assets/js/102.c9485235.js"><link rel="prefetch" href="/assets/js/103.d88a3805.js"><link rel="prefetch" href="/assets/js/104.6e034144.js"><link rel="prefetch" href="/assets/js/105.d22f7450.js"><link rel="prefetch" href="/assets/js/106.a6cb54b0.js"><link rel="prefetch" href="/assets/js/107.7b65e72b.js"><link rel="prefetch" href="/assets/js/108.eb5804bb.js"><link rel="prefetch" href="/assets/js/11.c54ae13c.js"><link rel="prefetch" href="/assets/js/110.51d3d641.js"><link rel="prefetch" href="/assets/js/111.022b64a7.js"><link rel="prefetch" href="/assets/js/112.da8afd52.js"><link rel="prefetch" href="/assets/js/113.05a17b18.js"><link rel="prefetch" href="/assets/js/114.8960d913.js"><link rel="prefetch" href="/assets/js/115.67919f68.js"><link rel="prefetch" href="/assets/js/116.62b0cd71.js"><link rel="prefetch" href="/assets/js/117.ebac3eff.js"><link rel="prefetch" href="/assets/js/118.ecd629bd.js"><link rel="prefetch" href="/assets/js/119.a09a0897.js"><link rel="prefetch" href="/assets/js/12.60aa3b24.js"><link rel="prefetch" href="/assets/js/120.bf639d3d.js"><link rel="prefetch" href="/assets/js/121.b89d0c8e.js"><link rel="prefetch" href="/assets/js/122.1a75ff83.js"><link rel="prefetch" href="/assets/js/123.d2127132.js"><link rel="prefetch" href="/assets/js/124.2caff9e0.js"><link rel="prefetch" href="/assets/js/125.9b9f966a.js"><link rel="prefetch" href="/assets/js/126.58cdfb3d.js"><link rel="prefetch" href="/assets/js/127.8ef09c53.js"><link rel="prefetch" href="/assets/js/128.efdc2ae4.js"><link rel="prefetch" href="/assets/js/129.e35cbc57.js"><link rel="prefetch" href="/assets/js/13.125c13a0.js"><link rel="prefetch" href="/assets/js/130.f01a55e3.js"><link rel="prefetch" href="/assets/js/131.65205f4a.js"><link rel="prefetch" href="/assets/js/132.f42c5a0a.js"><link rel="prefetch" href="/assets/js/133.9ba468b3.js"><link rel="prefetch" href="/assets/js/134.7b597ba9.js"><link rel="prefetch" href="/assets/js/135.fb828b9a.js"><link rel="prefetch" href="/assets/js/136.3887532f.js"><link rel="prefetch" href="/assets/js/137.549bae01.js"><link rel="prefetch" href="/assets/js/138.db8d423d.js"><link rel="prefetch" href="/assets/js/139.dbaf2267.js"><link rel="prefetch" href="/assets/js/14.bd1d0b0d.js"><link rel="prefetch" href="/assets/js/140.6cb65fdc.js"><link rel="prefetch" href="/assets/js/141.9bd6cc4b.js"><link rel="prefetch" href="/assets/js/142.552db5ed.js"><link rel="prefetch" href="/assets/js/143.2c9f2bf4.js"><link rel="prefetch" href="/assets/js/144.fba98a15.js"><link rel="prefetch" href="/assets/js/145.c42f3a21.js"><link rel="prefetch" href="/assets/js/146.596d4d33.js"><link rel="prefetch" href="/assets/js/147.c48ae5c1.js"><link rel="prefetch" href="/assets/js/148.71064871.js"><link rel="prefetch" href="/assets/js/149.16582d21.js"><link rel="prefetch" href="/assets/js/15.f247873b.js"><link rel="prefetch" href="/assets/js/150.ead09aca.js"><link rel="prefetch" href="/assets/js/151.971fdf4b.js"><link rel="prefetch" href="/assets/js/152.369c9362.js"><link rel="prefetch" href="/assets/js/153.371edd15.js"><link rel="prefetch" href="/assets/js/154.e090b491.js"><link rel="prefetch" href="/assets/js/155.c68bf602.js"><link rel="prefetch" href="/assets/js/156.304aea8d.js"><link rel="prefetch" href="/assets/js/157.83beef7f.js"><link rel="prefetch" href="/assets/js/158.bb1794b0.js"><link rel="prefetch" href="/assets/js/159.2d54e792.js"><link rel="prefetch" href="/assets/js/16.04336c71.js"><link rel="prefetch" href="/assets/js/160.99d56586.js"><link rel="prefetch" href="/assets/js/161.edf660aa.js"><link rel="prefetch" href="/assets/js/162.0b84606e.js"><link rel="prefetch" href="/assets/js/163.b59e0d60.js"><link rel="prefetch" href="/assets/js/164.d9eb8228.js"><link rel="prefetch" href="/assets/js/165.ca624c79.js"><link rel="prefetch" href="/assets/js/166.025b2ba1.js"><link rel="prefetch" href="/assets/js/167.abc982cc.js"><link rel="prefetch" href="/assets/js/168.27ca13dc.js"><link rel="prefetch" href="/assets/js/169.41e753a2.js"><link rel="prefetch" href="/assets/js/17.43b3c1c8.js"><link rel="prefetch" href="/assets/js/170.626319e1.js"><link rel="prefetch" href="/assets/js/171.a221dddf.js"><link rel="prefetch" href="/assets/js/172.464b2361.js"><link rel="prefetch" href="/assets/js/173.96a3afee.js"><link rel="prefetch" href="/assets/js/174.116607c2.js"><link rel="prefetch" href="/assets/js/175.ea3e8659.js"><link rel="prefetch" href="/assets/js/176.7d7b8afc.js"><link rel="prefetch" href="/assets/js/177.a6e00aa0.js"><link rel="prefetch" href="/assets/js/178.1f93afaf.js"><link rel="prefetch" href="/assets/js/179.3aa00dcd.js"><link rel="prefetch" href="/assets/js/18.d81b44d5.js"><link rel="prefetch" href="/assets/js/180.f8b2b75a.js"><link rel="prefetch" href="/assets/js/181.8e11258a.js"><link rel="prefetch" href="/assets/js/182.22243941.js"><link rel="prefetch" href="/assets/js/183.d051fdf6.js"><link rel="prefetch" href="/assets/js/184.a994075e.js"><link rel="prefetch" href="/assets/js/185.776c7e16.js"><link rel="prefetch" href="/assets/js/186.f1887955.js"><link rel="prefetch" href="/assets/js/187.da0d3626.js"><link rel="prefetch" href="/assets/js/188.8dfc358f.js"><link rel="prefetch" href="/assets/js/189.dcac5a59.js"><link rel="prefetch" href="/assets/js/19.1b3d66e1.js"><link rel="prefetch" href="/assets/js/190.c7e413d0.js"><link rel="prefetch" href="/assets/js/191.d9806121.js"><link rel="prefetch" href="/assets/js/192.869791da.js"><link rel="prefetch" href="/assets/js/193.2d74c4c8.js"><link rel="prefetch" href="/assets/js/194.c73a1909.js"><link rel="prefetch" href="/assets/js/195.e8c74834.js"><link rel="prefetch" href="/assets/js/20.bd5949ec.js"><link rel="prefetch" href="/assets/js/21.3fcf98cf.js"><link rel="prefetch" href="/assets/js/22.2fa1e2e8.js"><link rel="prefetch" href="/assets/js/23.1ae64bb4.js"><link rel="prefetch" href="/assets/js/24.7bdf7387.js"><link rel="prefetch" href="/assets/js/25.392c436e.js"><link rel="prefetch" href="/assets/js/26.58acbd4b.js"><link rel="prefetch" href="/assets/js/27.c725bdd5.js"><link rel="prefetch" href="/assets/js/28.6c9bda1e.js"><link rel="prefetch" href="/assets/js/29.e656b537.js"><link rel="prefetch" href="/assets/js/30.2c326fc7.js"><link rel="prefetch" href="/assets/js/31.e6c9fa30.js"><link rel="prefetch" href="/assets/js/32.c9c88437.js"><link rel="prefetch" href="/assets/js/33.0c53373c.js"><link rel="prefetch" href="/assets/js/34.9821e543.js"><link rel="prefetch" href="/assets/js/35.de8253eb.js"><link rel="prefetch" href="/assets/js/36.d182f929.js"><link rel="prefetch" href="/assets/js/37.9fa79014.js"><link rel="prefetch" href="/assets/js/38.9bebff76.js"><link rel="prefetch" href="/assets/js/39.19a3a2d4.js"><link rel="prefetch" href="/assets/js/4.564edb9d.js"><link rel="prefetch" href="/assets/js/40.cca6955f.js"><link rel="prefetch" href="/assets/js/41.854cd09a.js"><link rel="prefetch" href="/assets/js/42.ca7b612f.js"><link rel="prefetch" href="/assets/js/43.87027d58.js"><link rel="prefetch" href="/assets/js/44.8c2b4f4b.js"><link rel="prefetch" href="/assets/js/45.dffb4e08.js"><link rel="prefetch" href="/assets/js/46.f58049a5.js"><link rel="prefetch" href="/assets/js/47.6854070c.js"><link rel="prefetch" href="/assets/js/48.6cd9fa3d.js"><link rel="prefetch" href="/assets/js/49.e8861afa.js"><link rel="prefetch" href="/assets/js/5.5c31d62f.js"><link rel="prefetch" href="/assets/js/50.703bffab.js"><link rel="prefetch" href="/assets/js/51.6655c373.js"><link rel="prefetch" href="/assets/js/52.deb2eb09.js"><link rel="prefetch" href="/assets/js/53.6e0ed77d.js"><link rel="prefetch" href="/assets/js/54.b05c58ad.js"><link rel="prefetch" href="/assets/js/55.49c8164e.js"><link rel="prefetch" href="/assets/js/56.a5574e6b.js"><link rel="prefetch" href="/assets/js/57.58cb0de4.js"><link rel="prefetch" href="/assets/js/58.52345112.js"><link rel="prefetch" href="/assets/js/59.663ce78d.js"><link rel="prefetch" href="/assets/js/6.a9df34ee.js"><link rel="prefetch" href="/assets/js/60.f06adde2.js"><link rel="prefetch" href="/assets/js/61.170255a1.js"><link rel="prefetch" href="/assets/js/62.9d120050.js"><link rel="prefetch" href="/assets/js/63.70cced6b.js"><link rel="prefetch" href="/assets/js/64.577f3548.js"><link rel="prefetch" href="/assets/js/65.c037b29d.js"><link rel="prefetch" href="/assets/js/66.7dd1045f.js"><link rel="prefetch" href="/assets/js/67.d3aa4d6c.js"><link rel="prefetch" href="/assets/js/68.526dbb61.js"><link rel="prefetch" href="/assets/js/69.58269266.js"><link rel="prefetch" href="/assets/js/7.6609d4d6.js"><link rel="prefetch" href="/assets/js/70.64108f1b.js"><link rel="prefetch" href="/assets/js/71.1e95e0a6.js"><link rel="prefetch" href="/assets/js/72.42e7ec94.js"><link rel="prefetch" href="/assets/js/73.dad4e1c5.js"><link rel="prefetch" href="/assets/js/74.28ea286a.js"><link rel="prefetch" href="/assets/js/75.dd6d4c6f.js"><link rel="prefetch" href="/assets/js/76.ca6539df.js"><link rel="prefetch" href="/assets/js/77.feb13b0e.js"><link rel="prefetch" href="/assets/js/78.321e90e6.js"><link rel="prefetch" href="/assets/js/79.68eb8fcf.js"><link rel="prefetch" href="/assets/js/8.396d51fd.js"><link rel="prefetch" href="/assets/js/80.4edb5321.js"><link rel="prefetch" href="/assets/js/81.735d7e57.js"><link rel="prefetch" href="/assets/js/82.fa120bdf.js"><link rel="prefetch" href="/assets/js/83.bf755f94.js"><link rel="prefetch" href="/assets/js/84.9b32070c.js"><link rel="prefetch" href="/assets/js/85.592aca7c.js"><link rel="prefetch" href="/assets/js/86.4dcd9e73.js"><link rel="prefetch" href="/assets/js/87.a9e546aa.js"><link rel="prefetch" href="/assets/js/88.2a423212.js"><link rel="prefetch" href="/assets/js/89.5f455115.js"><link rel="prefetch" href="/assets/js/9.adb074c6.js"><link rel="prefetch" href="/assets/js/90.5202da0a.js"><link rel="prefetch" href="/assets/js/91.02cee99d.js"><link rel="prefetch" href="/assets/js/92.f16bad0b.js"><link rel="prefetch" href="/assets/js/93.f933634f.js"><link rel="prefetch" href="/assets/js/94.8e7b1d65.js"><link rel="prefetch" href="/assets/js/95.ee0e4a0a.js"><link rel="prefetch" href="/assets/js/96.e21d78c2.js"><link rel="prefetch" href="/assets/js/97.c87e514e.js"><link rel="prefetch" href="/assets/js/98.d123ac92.js"><link rel="prefetch" href="/assets/js/99.92d1b416.js">
    <link rel="stylesheet" href="/assets/css/0.styles.91f57736.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container" data-v-3ba18f14><div data-v-3ba18f14><div id="loader-wrapper" class="loading-wrapper" data-v-041fef5b data-v-3ba18f14 data-v-3ba18f14><div class="loader-main" data-v-041fef5b><div data-v-041fef5b></div><div data-v-041fef5b></div><div data-v-041fef5b></div><div data-v-041fef5b></div></div> <!----> <!----></div> <div class="password-shadow password-wrapper-out" style="display:none;" data-v-68139a52 data-v-3ba18f14 data-v-3ba18f14><h3 class="title" style="display:none;" data-v-68139a52 data-v-68139a52>JavaKeeper</h3> <!----> <label id="box" class="inputBox" style="display:none;" data-v-68139a52 data-v-68139a52><input type="password" value="" data-v-68139a52> <span data-v-68139a52>Konck! Knock!</span> <button data-v-68139a52>OK</button></label> <div class="footer" style="display:none;" data-v-68139a52 data-v-68139a52><span data-v-68139a52><i class="iconfont reco-theme" data-v-68139a52></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-68139a52>vuePress-theme-reco</a></span> <span data-v-68139a52><i class="iconfont reco-copyright" data-v-68139a52></i> <a data-v-68139a52><span data-v-68139a52>海星</span>
            
          <!---->
          2020
        </a></span></div></div> <div class="hide" data-v-3ba18f14><header class="navbar" data-v-3ba18f14><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">JavaKeeper</span></a> <div class="links"><div class="color-picker"><a class="color-button"><i class="iconfont reco-color"></i></a> <div class="color-picker-menu" style="display:none;"><div class="mode-options"><h4 class="title">Choose mode</h4> <ul class="color-mode-options"><li class="dark">dark</li><li class="auto active">auto</li><li class="light">light</li></ul></div></div></div> <div class="search-box"><i class="iconfont reco-search"></i> <input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/java/" class="nav-link"><i class="iconfont undefined"></i>
  Java
</a></div><div class="nav-item"><a href="/data-structure-algorithms/" class="nav-link"><i class="iconfont undefined"></i>
  数据结构与算法
</a></div><div class="nav-item"><a href="/data-store/" class="nav-link"><i class="iconfont undefined"></i>
  数据存储与缓存
</a></div><div class="nav-item"><a href="/interview/" class="nav-link router-link-active"><i class="iconfont undefined"></i>
  直击面试
</a></div> <a href="https://github.com/Jstarfish/JavaKeeper" target="_blank" rel="noopener noreferrer" class="repo-link"><i class="iconfont reco-github"></i>
    GitHub
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header> <div class="sidebar-mask" data-v-3ba18f14></div> <aside class="sidebar" data-v-3ba18f14><div class="personal-info-wrapper" data-v-5f6acefd data-v-3ba18f14><!----> <h3 class="name" data-v-5f6acefd>
    海星
  </h3> <div class="num" data-v-5f6acefd><div data-v-5f6acefd><h3 data-v-5f6acefd>0</h3> <h6 data-v-5f6acefd>Article</h6></div> <div data-v-5f6acefd><h3 data-v-5f6acefd>0</h3> <h6 data-v-5f6acefd>Tag</h6></div></div> <hr data-v-5f6acefd></div> <nav class="nav-links"><div class="nav-item"><a href="/java/" class="nav-link"><i class="iconfont undefined"></i>
  Java
</a></div><div class="nav-item"><a href="/data-structure-algorithms/" class="nav-link"><i class="iconfont undefined"></i>
  数据结构与算法
</a></div><div class="nav-item"><a href="/data-store/" class="nav-link"><i class="iconfont undefined"></i>
  数据存储与缓存
</a></div><div class="nav-item"><a href="/interview/" class="nav-link router-link-active"><i class="iconfont undefined"></i>
  直击面试
</a></div> <a href="https://github.com/Jstarfish/JavaKeeper" target="_blank" rel="noopener noreferrer" class="repo-link"><i class="iconfont reco-github"></i>
    GitHub
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav> <ul class="sidebar-links"><li><a href="/interview/Collections-FAQ.html" class="sidebar-link">Java集合面试</a></li><li><a href="/interview/JUC-FAQ.html" class="sidebar-link">Java 多线程面试</a></li><li><a href="/interview/JVM-FAQ.html" class="sidebar-link">JVM 面试</a></li><li><a href="/interview/MySQL-FAQ.html" class="sidebar-link">MySQL 面试</a></li><li><a href="/interview/Redis-FAQ.html" class="sidebar-link">Redis 面试</a></li><li><a href="/interview/Network-FAQ.html" class="sidebar-link">计算机网络面试</a></li><li><a href="/interview/Kafka-FAQ.html" class="sidebar-link">Kafka 面试</a></li><li><a href="/interview/ZooKeeper-FAQ.html" class="sidebar-link">Zookeeper 面试</a></li><li><a href="/interview/MyBatis-FAQ.html" class="sidebar-link">MyBatis 面试</a></li><li><a href="/interview/Spring-FAQ.html" aria-current="page" class="active sidebar-link">Spring 面试</a></li><li><a href="/interview/Design-Pattern-FAQ.html" class="sidebar-link">设计模式面试</a></li><li><a href="/interview/Tomcat-FAQ.html" class="sidebar-link">Tomcat 面试</a></li></ul> </aside> <div class="password-shadow password-wrapper-in" style="display:none;" data-v-68139a52 data-v-3ba18f14><h3 class="title" style="display:none;" data-v-68139a52 data-v-68139a52></h3> <!----> <label id="box" class="inputBox" style="display:none;" data-v-68139a52 data-v-68139a52><input type="password" value="" data-v-68139a52> <span data-v-68139a52>Konck! Knock!</span> <button data-v-68139a52>OK</button></label> <div class="footer" style="display:none;" data-v-68139a52 data-v-68139a52><span data-v-68139a52><i class="iconfont reco-theme" data-v-68139a52></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-68139a52>vuePress-theme-reco</a></span> <span data-v-68139a52><i class="iconfont reco-copyright" data-v-68139a52></i> <a data-v-68139a52><span data-v-68139a52>海星</span>
            
          <!---->
          2020
        </a></span></div></div> <div data-v-3ba18f14><main class="page"><div class="page-title" style="display:none;"><h1 class="title"></h1> <div data-v-5d8dbdb4><i class="iconfont reco-account" data-v-5d8dbdb4><span data-v-5d8dbdb4>海星</span></i> <!----> <!----> <!----></div></div> <div class="theme-reco-content content__default" style="display:none;"><blockquote><p>基于Spring Framework 4.x 总结的常见面试题，系统学习建议还是官方文档走起：https://spring.io/projects/spring-framework#learn</p></blockquote> <h2 id="一、一般问题"><a href="#一、一般问题" class="header-anchor">#</a> 一、一般问题</h2> <h3 id="开发中主要使用-spring-的什么技术"><a href="#开发中主要使用-spring-的什么技术" class="header-anchor">#</a> 开发中主要使用 Spring 的什么技术 ?</h3> <ol><li>IOC 容器管理各层的组件</li> <li>使用 AOP 配置声明式事务</li> <li>整合其他框架</li></ol> <h3 id="spring有哪些优点"><a href="#spring有哪些优点" class="header-anchor">#</a> Spring有哪些优点？</h3> <ul><li><strong>轻量级</strong>：Spring在大小和透明性方面绝对属于轻量级的，基础版本的Spring框架大约只有2MB。</li> <li><strong>控制反转(IOC)</strong>：Spring使用控制反转技术实现了松耦合。依赖被注入到对象，而不是创建或寻找依赖对象。</li> <li><strong>面向切面编程(AOP)</strong>：Spring支持面向切面编程，同时把应用的业务逻辑与系统的服务分离开来。</li> <li><strong>容器</strong>：Spring包含并管理应用程序对象的配置及生命周期。</li> <li><strong>MVC框架</strong>：Spring的web框架是一个设计优良的web MVC框架，很好的取代了一些web框架。</li> <li><strong>事务管理</strong>：Spring对下至本地业务上至全局业务(JAT)提供了统一的事务管理接口。</li> <li><strong>异常处理</strong>：Spring提供一个方便的API将特定技术的异常(由JDBC, Hibernate, 或JDO抛出)转化为一致的、Unchecked异常。</li></ul> <h3 id="spring模块"><a href="#spring模块" class="header-anchor">#</a> Spring模块</h3> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8npk820uj30k00f0q3x.jpg" alt="spring overview"></p> <h3 id="简述-aop-和-ioc-概念"><a href="#简述-aop-和-ioc-概念" class="header-anchor">#</a> 简述 AOP 和 IOC 概念</h3> <p>AOP：Aspect Oriented Program, 面向(方面)切面的编程;Filter(过滤器)也是一种 AOP. AOP 是一种新的 方法论, 是对传统 OOP(Object-OrientedProgramming, 面向对象编程) 的补充. AOP 的主要编程对象是切面(aspect),而切面模块化横切关注点.可以举例通过事务说明.</p> <p>IOC：Invert Of Control, 控制反转. 也称为 DI(依赖注入)其思想是反转资源获取的方向. 传统的资源查找方式要求组件向容器发起请求查找资源.作为回应, 容器适时的返回资源. 而应用了 IOC 之后, 则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源. 这种行为也被称为查找的被动形式</p> <h2 id="二、依赖注入"><a href="#二、依赖注入" class="header-anchor">#</a> 二、依赖注入</h2> <p>IoC（Inverse of Control:控制反转）是一种<strong>设计思想</strong>，就是 <strong>将原本在程序中手动创建对象的控制权，交由Spring框架来管理。</strong> IoC 在其他语言中也有应用，并非 Spring 特有。 <strong>IoC 容器是 Spring 用来实现 IoC 的载体， IoC 容器实际上就是个Map（key，value）,Map 中存放的是各种对象。</strong></p> <p>将对象之间的相互依赖关系交给 IoC 容器来管理，并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发，把应用从复杂的依赖关系中解放出来。 <strong>IoC 容器就像是一个工厂一样，当我们需要创建一个对象的时候，只需要配置好配置文件/注解即可，完全不用考虑对象是如何被创建出来的。</strong> 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层，假如我们需要实例化这个 Service，你可能要每次都要搞清这个 Service 所有底层类的构造函数，这可能会把人逼疯。如果利用 IoC 的话，你只需要配置好，然后在需要的地方引用就行了，这大大增加了项目的可维护性且降低了开发难度。</p> <h3 id="什么是-spring-ioc-容器"><a href="#什么是-spring-ioc-容器" class="header-anchor">#</a> 什么是 Spring IOC 容器？</h3> <p>Spring 框架的核心是 Spring 容器。容器创建对象，将它们装配在一起，配置它们并管理它们的完整生命周期。Spring 容器使用依赖注入来管理组成应用程序的组件。容器通过读取提供的配置元数据来接收对象进行实例化，配置和组装的指令。该元数据可以通过 XML，Java 注解或 Java 代码提供。</p> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8npp9px7j30du088743.jpg" alt="container magic"></p> <h3 id="什么是依赖注入"><a href="#什么是依赖注入" class="header-anchor">#</a> 什么是依赖注入？</h3> <p><strong>依赖注入（DI,Dependency Injection）是在编译阶段尚未知所需的功能是来自哪个的类的情况下，将其他对象所依赖的功能对象实例化的模式</strong>。这就需要一种机制用来激活相应的组件以提供特定的功能，所以<strong>依赖注入是控制反转的基础</strong>。否则如果在组件不受框架控制的情况下，框架又怎么知道要创建哪个组件？</p> <p>依赖注入有以下三种实现方式：</p> <ol><li>构造器注入</li> <li>Setter方法注入（属性注入）</li> <li>接口注入</li></ol> <h3 id="spring-中有多少种-ioc-容器"><a href="#spring-中有多少种-ioc-容器" class="header-anchor">#</a> Spring 中有多少种 IOC 容器？</h3> <p>在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前，必须对它进行实例化。只有在容器实例化后， 才可以从 IOC 容器里获取 Bean 实例并使用</p> <p>Spring 提供了两种类型的 IOC 容器实现</p> <ul><li><p>BeanFactory：IOC 容器的基本实现</p></li> <li><p>ApplicationContext：提供了更多的高级特性，是 BeanFactory 的子接口</p></li></ul> <p>BeanFactory 是 Spring 框架的基础设施，面向 Spring 本身；ApplicationContext 面向使用 Spring 框架的开发者，几乎所有的应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory；</p> <p>无论使用何种方式, 配置文件是相同的。</p> <h3 id="beanfactory-和-applicationcontext-区别"><a href="#beanfactory-和-applicationcontext-区别" class="header-anchor">#</a> BeanFactory 和 ApplicationContext 区别</h3> <table><thead><tr><th>BeanFactory</th> <th>ApplicationContext</th></tr></thead> <tbody><tr><td>懒加载</td> <td>即时加载</td></tr> <tr><td>它使用语法显式提供资源对象</td> <td>它自己创建和管理资源对象</td></tr> <tr><td>不支持国际化</td> <td>支持国际化</td></tr> <tr><td>不支持基于依赖的注解</td> <td>支持基于依赖的注解</td></tr></tbody></table> <p><strong>ApplicationContext</strong></p> <p>ApplicationContext 的主要实现类：</p> <ul><li>ClassPathXmlApplicationContext：从类路径下加载配置文件</li> <li>FileSystemXmlApplicationContext: 从文件系统中加载配置文件</li> <li>ConfigurableApplicationContext 扩展于 ApplicationContext，新增加两个主要方法：refresh() 和 close()， 让 ApplicationContext具有启动、刷新和关闭上下文的能力</li> <li>WebApplicationContext 是专门为 WEB 应用而准备的，它允许从相对于 WEB 根目录的路径中完成初始化工作</li> <li>ApplicationContext 在初始化上下文时就实例化所有单例的 Bean</li></ul> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8npy9i8cj31300pwn0i.jpg" alt="javadoop.com"></p> <p><strong>从 IOC 容器中获取 Bean</strong></p> <ul><li>调用 ApplicationContext 的 getBean() 方法</li></ul> <div class="language-java extra-class"><pre class="language-java"><code><span class="token class-name">ApplicationContext</span> ctx <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassPathXmlApplicationContext</span><span class="token punctuation">(</span><span class="token string">&quot;beans.xml&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">HelloWorld</span> helloWorld <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">HelloWorld</span><span class="token punctuation">)</span> ctx<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span><span class="token string">&quot;helloWorld&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
helloWorld<span class="token punctuation">.</span><span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h3 id="列举-ioc-的一些好处"><a href="#列举-ioc-的一些好处" class="header-anchor">#</a> 列举 IoC 的一些好处</h3> <ul><li>它将最小化应用程序中的代码量；</li> <li>它将使您的应用程序易于测试，因为它不需要单元测试用例中的任何单例或 JNDI 查找机制；</li> <li>它以最小的影响和最少的侵入机制促进松耦合；</li> <li>它支持即时的实例化和延迟加载服务</li></ul> <h3 id="spring-ioc-的实现机制"><a href="#spring-ioc-的实现机制" class="header-anchor">#</a> Spring IoC 的实现机制</h3> <p>Spring 中的 IoC 的实现原理就是工厂模式加反射机制，示例：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">interface</span> <span class="token class-name">Fruit</span> <span class="token punctuation">{</span>
     <span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token keyword">void</span> <span class="token function">eat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Apple</span> <span class="token keyword">implements</span> <span class="token class-name">Fruit</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">eat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;Apple&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Orange</span> <span class="token keyword">implements</span> <span class="token class-name">Fruit</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">eat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;Orange&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Factory</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Fruit</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token class-name">String</span> <span class="token class-name">ClassName</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">Fruit</span> f<span class="token operator">=</span><span class="token keyword">null</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            f<span class="token operator">=</span><span class="token punctuation">(</span><span class="token class-name">Fruit</span><span class="token punctuation">)</span><span class="token class-name">Class</span><span class="token punctuation">.</span><span class="token function">forName</span><span class="token punctuation">(</span><span class="token class-name">ClassName</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">newInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> f<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Client</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> a<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">Fruit</span> f<span class="token operator">=</span><span class="token class-name">Factory</span><span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token string">&quot;priv.starfish.spring.Apple&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>f<span class="token operator">!=</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
            f<span class="token punctuation">.</span><span class="token function">eat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="三、beans"><a href="#三、beans" class="header-anchor">#</a> 三、Beans</h2> <h3 id="什么是-spring-beans"><a href="#什么是-spring-beans" class="header-anchor">#</a> 什么是 Spring Beans？</h3> <ul><li>它们是构成用户应用程序主干的对象</li> <li>Bean 由 Spring IoC 容器管理</li> <li>它们由 Spring IoC 容器实例化，配置，装配和管理</li> <li>Bean 是基于用户提供给容器的配置元数据创建</li></ul> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8nq56qklj30l602jjre.jpg" alt=""></p> <h3 id="spring-提供了哪些配置方式"><a href="#spring-提供了哪些配置方式" class="header-anchor">#</a> Spring 提供了哪些配置方式？</h3> <ul><li><p>基于 xml 配置</p> <p>bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件通常包含许多 bean 定义和特定于应用程序的配置选项。它们通常以 bean 标签开头。例如：</p> <div class="language-xml extra-class"><pre class="language-xml"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>studentbean<span class="token punctuation">&quot;</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>org.edureka.firstSpring.StudentBean<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
 <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>property</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>name<span class="token punctuation">&quot;</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>Edureka<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>property</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bean</span><span class="token punctuation">&gt;</span></span>
</code></pre></div></li> <li><p>基于注解配置</p> <p>您可以通过在相关的类，方法或字段声明上使用注解，将 bean 配置为组件类本身，而不是使用 XML 来描述 bean 装配。默认情况下，Spring 容器中未打开注解装配。因此，您需要在使用它之前在 Spring 配置文件中启用它。例如：</p> <div class="language-xml extra-class"><pre class="language-xml"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>beans</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">context:</span>annotation-config</span><span class="token punctuation">/&gt;</span></span>
<span class="token comment">&lt;!-- bean definitions go here --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>beans</span><span class="token punctuation">&gt;</span></span>
</code></pre></div></li> <li><p>基于 Java API 配置</p> <p>Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现。</p> <ol><li><p>@Bean 注解扮演与 <code>&lt;bean/&gt;</code> 元素相同的角色。</p></li> <li><p>@Configuration 类允许通过简单地调用同一个类中的其他 @Bean 方法来定义 bean 间依赖关系。</p></li></ol></li></ul> <p>例如：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token annotation punctuation">@Configuration</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StudentConfig</span> <span class="token punctuation">{</span>
    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">StudentBean</span> <span class="token function">myStudent</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">StudentBean</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="spring-bean的作用域"><a href="#spring-bean的作用域" class="header-anchor">#</a> Spring Bean的作用域？</h3> <ul><li><p>在 Spring 中, 可以在 &lt;bean&gt; 元素的 scope 属性里设置 Bean 的作用域。</p></li> <li><p>默认情况下，Spring 只为每个在 IOC 容器里声明的 Bean 创建唯一一个实例，整个 IOC 容器范围内都能共享该实例：所有后续的 <code>getBean()</code> 调用和 Bean 引用都将返回这个唯一的 Bean 实例。该作用域被称为 <strong>singleton</strong>，它是所有 Bean 的默认作用域。</p></li></ul> <p>Spring 容器中的 bean 可以分为 5 个范围。所有范围的名称都是自说明的，但是为了避免混淆，还是让我们来解释一下：</p> <ol><li><strong>singleton</strong>：这种bean范围是默认的，这种范围确保不管接受到多少个请求，每个容器中只有一个bean的实例，单例的模式由bean factory自身来维护。</li> <li><strong>prototype</strong>：原型范围与单例范围相反，为每一个bean请求提供一个实例。</li> <li><strong>request</strong>：每次HTTP请求都会创建一个新的bean，该作用于仅适用于WebApplicationContext环境，在请求完成以后，bean会失效并被垃圾回收器回收。</li> <li><strong>Session</strong>：同一个HTTP Session 共享一个bean，不同的 HTTP Session使用不同的bean。该作用于仅适用于WebApplicationContext环境，在session过期后，bean会随之失效。</li> <li><strong>global-session</strong>：全局session作用域，仅仅在基于portlet的web应用中才有意义，Spring5已经没有了。Portlet是能够生成语义代码(例如：HTML)片段的小型Java Web插件。它们基于portlet容器，可以像servlet一样处理HTTP请求。但是，与 servlet 不同，每个 portlet 都有不同的会话</li></ol> <p>全局作用域与Servlet中的session作用域效果相同。</p> <h3 id="spring-bean-容器的生命周期是什么样的"><a href="#spring-bean-容器的生命周期是什么样的" class="header-anchor">#</a> Spring bean 容器的生命周期是什么样的？</h3> <p>Spring IOC 容器可以管理 Bean 的生命周期, Spring 允许在 Bean 生命周期的特定点执行定制的任务。</p> <p>Spring bean 容器的生命周期流程如下：</p> <ol><li>Spring 容器根据配置中的 bean 定义实例化 bean；</li> <li>Spring 使用依赖注入填充所有属性，如 bean 中所定义的配置；</li> <li>如果 bean 实现 BeanNameAware 接口，则工厂通过传递 bean 的 ID 来调用 setBeanName()；</li> <li>如果 bean 实现 BeanFactoryAware 接口，工厂通过传递自身的实例来调用 setBeanFactory()；</li> <li>与上面的类似，如果实现了其他 <code>*.Aware</code>接口，就调用相应的方法；</li> <li>如果存在与 bean 关联的任何 BeanPostProcessors，则调用 preProcessBeforeInitialization() 方法；</li> <li>如果为 bean 指定了 init 方法（<code>&lt;bean&gt;</code> 的 init-method 属性），那么将调用它；</li> <li>最后，如果存在与 bean 关联的任何 BeanPostProcessors，则将调用 postProcessAfterInitialization() 方法；</li> <li>如果 bean 实现 DisposableBean 接口，当 spring 容器关闭时，会调用 destory()；</li> <li>如果为 bean 指定了 destroy 方法（<code>&lt;bean&gt;</code> 的 destroy-method 属性），那么将调用它</li></ol> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8nqbdmr2j30zp0u0n2x.jpg" alt=""></p> <p>在 bean 初始化时会经历几个阶段，要与容器对 bean 生命周期的管理交互，可以实现  <code>InitializingBean</code> 和 <code>DisposableBean</code> 接口。容器对前者调用 <code>afterPropertiesSet()</code>，对后者调用 <code>destroy()</code>，以允许 bean 在初始化和销毁 bean 时执行某些操作。</p> <p>官方不建议使用这两个接口，而是建议使用 <code>@PostConstruct</code> 和 <code>@PreDestroy</code>，或者 XML 配置中使用 <code>init-method</code>和<code>destroy-method</code> 属性</p> <div class="language-xml extra-class"><pre class="language-xml"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>exampleInitBean<span class="token punctuation">&quot;</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>examples.ExampleBean<span class="token punctuation">&quot;</span></span> <span class="token attr-name">init-method</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>init<span class="token punctuation">&quot;</span></span><span class="token punctuation">/&gt;</span></span>
</code></pre></div><div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ExampleBean</span> <span class="token punctuation">{</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// do some initialization work</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>等价于</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AnotherExampleBean</span> <span class="token keyword">implements</span> <span class="token class-name">InitializingBean</span> <span class="token punctuation">{</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">afterPropertiesSet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// do some initialization work</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><blockquote><p>Spring Bean生命周期回调——初始化回调和销毁回调方法</p></blockquote> <p>实现 Bean 初始化回调和销毁回调各有三种方法，一是实现接口方法，二是在XML配置，三是使用注解</p> <ul><li>使用注解 <code>@PostConstruct</code> 和 <code>@PreDestroy</code></li> <li>实现  <code>InitializingBean</code> 和 <code>DisposableBean</code> 接口</li> <li>XML 中配置 <code>init-method</code> 和 <code>destroy-method</code></li></ul> <p>在一个 bean 中，如果配置了多种生命周期回调机制，会按照上边从上到下的次序调用</p> <h3 id="在-spring-中如何配置-bean"><a href="#在-spring-中如何配置-bean" class="header-anchor">#</a> 在 Spring 中如何配置 Bean?</h3> <p>Bean 的配置方式: 通过全类名 （反射）、 通过工厂方法 （静态工厂方法 &amp; 实例工厂方法）、FactoryBean</p> <h3 id="什么是-spring-装配"><a href="#什么是-spring-装配" class="header-anchor">#</a> 什么是 Spring 装配</h3> <p>当 bean 在 Spring 容器中组合在一起时，它被称为装配或 bean 装配，装配是创建应用对象之间协作关系的行为。 Spring 容器需要知道需要什么 bean 以及容器应该如何使用依赖注入来将 bean 绑定在一起，同时装配 bean。</p> <p>依赖注入的本质就是装配，装配是依赖注入的具体行为。</p> <p>注入是实例化的过程，将创建的bean放在Spring容器中，分为属性注入（setter方式）、构造器注入</p> <h3 id="什么是bean自动装配"><a href="#什么是bean自动装配" class="header-anchor">#</a> 什么是bean自动装配？</h3> <p>Spring 容器可以自动配置相互协作 beans 之间的关联关系。这意味着 Spring 可以自动配置一个 bean 和其他协作bean 之间的关系，通过检查 BeanFactory 的内容里有没有使用&lt; property&gt;元素。</p> <p>在Spring框架中共有5种自动装配，让我们逐一分析</p> <ol><li><p><strong>no</strong>：这是Spring框架的默认设置，在该设置下自动装配是关闭的，开发者需要自行在beanautowire属性里指定自动装配的模式</p></li> <li><p><strong>byName</strong>：该选项可以根据bean名称设置依赖关系。当向一个bean中自动装配一个属性时，容器将根据bean的名称自动在在配置文件中查询一个匹配的bean。如果找到的话，就装配这个属性，如果没找到的话就报错。</p></li> <li><p><strong>byType</strong>：该选项可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性时，容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。如果找到的话，就装配这个属性，如果没找到的话就报错。</p></li> <li><p><strong>constructor</strong>：构造器的自动装配和byType模式类似，但是仅仅适用于与有构造器相同参数的bean，如果在容器中没有找到与构造器参数类型一致的bean，那么将会抛出异常。</p></li> <li><p><strong>autodetect</strong>：Spring首先尝试通过 <em>constructor</em> 使用自动装配来连接，如果它不执行，Spring 尝试通过 <em>byType</em> 来自动装配</p></li></ol> <h3 id="自动装配有什么局限"><a href="#自动装配有什么局限" class="header-anchor">#</a> 自动装配有什么局限？</h3> <ul><li>基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。</li> <li>装配依赖中若是出现匹配到多个bean（出现歧义性），装配将会失败</li></ul> <h3 id="通过注解的方式配置bean-什么是基于注解的容器配置"><a href="#通过注解的方式配置bean-什么是基于注解的容器配置" class="header-anchor">#</a> 通过注解的方式配置bean | 什么是基于注解的容器配置</h3> <p><strong>组件扫描</strong>(component scanning): Spring 能够从 classpath下自动扫描, 侦测和实例化具有特定注解的组件。</p> <p>特定组件包括:</p> <ul><li><strong>@Component</strong>：基本注解, 标识了一个受 Spring 管理的组件</li> <li><strong>@Respository</strong>：标识持久层组件</li> <li><strong>@Service</strong>：标识服务层(业务层)组件</li> <li><strong>@Controller</strong>： 标识表现层组件</li></ul> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8nqhucw4j31qq0j2aak.jpg" alt=""></p> <p>对于扫描到的组件,，Spring 有默认的命名策略：使用非限定类名,，第一个字母小写。也可以在注解中通过 value 属性值标识组件的名称。</p> <p>当在组件类上使用了特定的注解之后,，还需要在 Spring 的配置文件中声明 <code>&lt;context:component-scan&gt;</code>：</p> <ul><li><p><code>base-package</code> 属性指定一个需要扫描的基类包，Spring 容器将会扫描这个基类包里及其子包中的所有类</p></li> <li><p>当需要扫描多个包时, 可以使用逗号分隔</p></li> <li><p>如果仅希望扫描特定的类而非基包下的所有类，可使用 <code>resource-pattern</code> 属性过滤特定的类，示例：</p> <div class="language-xml extra-class"><pre class="language-xml"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">context:</span>component-scan</span> <span class="token attr-name">base-package</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>priv.starfish.front.web.controller<span class="token punctuation">&quot;</span></span>
	<span class="token attr-name">annotation-config</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>true<span class="token punctuation">&quot;</span></span> <span class="token attr-name">resource-pattern</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>autowire/*.class<span class="token punctuation">&quot;</span></span><span class="token punctuation">/&gt;</span></span>
</code></pre></div></li></ul> <h3 id="如何在-spring-中启动注解装配"><a href="#如何在-spring-中启动注解装配" class="header-anchor">#</a> 如何在 spring 中启动注解装配？</h3> <p>默认情况下，Spring 容器中未打开注解装配。因此，要使用基于注解装配，我们必须通过配置<code>&lt;context：annotation-config /&gt;</code> 元素在 Spring 配置文件中启用它。</p> <h2 id="四、aop"><a href="#四、aop" class="header-anchor">#</a> 四、AOP</h2> <blockquote><p>👴：描述一下Spring AOP 呗？</p> <p>​		你有没有⽤过Spring的AOP? 是⽤来⼲嘛的? ⼤概会怎么使⽤？</p></blockquote> <h3 id="什么是-aop"><a href="#什么是-aop" class="header-anchor">#</a> 什么是 AOP？</h3> <p>AOP(Aspect-Oriented Programming，面向切面编程)：是一种新的方法论，是对传统 OOP(Object-Oriented Programming，面向对象编程) 的补充。在 OOP 中, 我们以类(class)作为我们的基本单元，而 AOP 中的基本单元是 <strong>Aspect(切面)</strong></p> <p>AOP 的主要编程对象是切面(aspect)</p> <p>在应用 AOP 编程时, 仍然需要定义公共功能，但可以明确的定义这个功能在哪里,，以什么方式应用,，并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。</p> <p>AOP 的好处:</p> <ul><li>每个事物逻辑位于一个位置，代码不分散，便于维护和升级</li> <li>业务模块更简洁, 只包含核心业务代码</li></ul> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8nqrqcgvj30ov0hqq38.jpg" alt=""></p> <h3 id="aop-术语"><a href="#aop-术语" class="header-anchor">#</a> <strong>AOP 术语</strong></h3> <ul><li>切面（Aspect）：横切关注点（跨越应用程序多个模块的功能），被模块化的特殊对象</li> <li>连接点（Joinpoint）：程序执行的某个特定位置，如类某个方法调用前、调用后、方法抛出异常后等。在这个位置我们可以插入一个 AOP 切面，它实际上是应用程序执行 Spring AOP 的位置</li></ul> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8nqwua8mj31da0fqmxb.jpg" alt=""></p> <ul><li>通知（Advice）： 通知是个在方法执行前或执行后要做的动作，实际上是程序执行时要通过 SpringAOP 框架触发的代码段。Spring 切面可以应用五种类型的通知：
<ul><li>before： 前置通知 ， 在一个方法执行前被调用</li> <li>after：在方法执行之后调用的通知，无论方式执行是否成功</li> <li>after-returning：仅当方法成功完成后执行的通知</li> <li>after-throwing：在方法抛出异常退出时执行的通知</li> <li>around：在方法执行之前和之后调用的通知</li></ul></li></ul> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8nr1x47wj30z20qi3yr.jpg" alt=""></p> <ul><li>目标（Target）：被通知的对象，通常是一个代理对象，也指被通知（advice）对象</li> <li>代理（Proxy）：向目标对象应用通知之后创建的对象</li> <li>切点（pointcut）：每个类都拥有多个连接点，程序运行中的一些时间点，例如一个方法的执行，或者是一个异常的处理。AOP 通过切点定位到特定的连接点。类比：连接点相当于数据库中的记录，切点相当于查询条件。切点和连接点不是一对一的关系，一个切点匹配多个连接点，切点通过 <code>org.springframework.aop.Pointcut</code> 接口进行描述，它使用类和方法作为连接点的查询条件</li> <li>引入（Introduction）：引入允许我们向现有的类添加新方法或属性</li> <li>织入（Weaving）：织入是把切面应用到目标对象并创建新的代理对象的过程</li></ul> <p><strong>Spring  AOP</strong></p> <ul><li>**AspectJ：**Java 社区里最完整最流行的 AOP 框架</li> <li>在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP</li></ul> <p><strong>在 Spring 中启用 AspectJ 注解支持</strong></p> <ul><li>要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库：<code>aopalliance.jar</code>、<code>aspectj.weaver.jar</code> 和 <code>spring-aspects.jar</code></li> <li>将 aop Schema 添加到 <code>&lt;beans&gt;</code> 根元素中.</li> <li>要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素 <code>&lt;aop:aspectj-autoproxy&gt;</code></li> <li>当 Spring IOC 容器侦测到 Bean 配置文件中的<code>&lt;aop:aspectj-autoproxy&gt;</code> 元素时, 会自动为与 AspectJ切面匹配的 Bean 创建代理.</li></ul> <h3 id="有哪写类型的通知-advice-用-aspectj-注解声明切面"><a href="#有哪写类型的通知-advice-用-aspectj-注解声明切面" class="header-anchor">#</a> 有哪写类型的通知（Advice） | 用 AspectJ 注解声明切面</h3> <ul><li><p>要在 Spring 中声明 AspectJ切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ切面之后, Spring IOC 容器就会为那些与 AspectJ切面相匹配的 Bean 创建代理.</p></li> <li><p>在 AspectJ注解中, 切面只是一个带有 @Aspect 注解的 Java 类.</p></li> <li><p>通知是标注有某种注解的简单的 Java 方法.</p></li> <li><p>AspectJ支持 5 种类型的通知注解:</p> <ul><li>@Before: 前置通知, 在方法执行之前执行</li> <li>@After: 后置通知, 在方法执行之后执行</li> <li>@AfterRunning: 返回通知, 在方法返回结果之后执行</li> <li>@AfterThrowing: 异常通知, 在方法抛出异常之后</li> <li>@Around: 环绕通知, 围绕着方法执行</li></ul></li></ul> <h3 id="aop-有哪些实现方式"><a href="#aop-有哪些实现方式" class="header-anchor">#</a> AOP 有哪些实现方式？</h3> <p>实现 AOP 的技术，主要分为两大类：</p> <ul><li>静态代理 - 指使用 AOP 框架提供的命令进行编译，从而在编译阶段就可生成 AOP 代理类，因此也称为编译时增强；
<ul><li>编译时编织（特殊编译器实现）</li> <li>类加载时编织（特殊的类加载器实现）。</li></ul></li> <li>动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类，因此也被称为运行时增强。
<ul><li>JDK 动态代理</li> <li>CGLIB</li></ul></li></ul> <h3 id="有哪些不同的aop实现"><a href="#有哪些不同的aop实现" class="header-anchor">#</a> 有哪些不同的AOP实现</h3> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8nr8kv0fj31kw0onwg3.jpg" alt=""></p> <h3 id="spring-aop-and-aspectj-aop-有什么区别"><a href="#spring-aop-and-aspectj-aop-有什么区别" class="header-anchor">#</a> Spring AOP and AspectJ AOP 有什么区别？</h3> <ul><li>Spring AOP 基于动态代理方式实现，AspectJ 基于静态代理方式实现。</li> <li>Spring AOP 仅支持方法级别的 PointCut；提供了完全的 AOP 支持，它还支持属性级别的 PointCut。</li></ul> <h2 id="五、数据访问"><a href="#五、数据访问" class="header-anchor">#</a> 五、数据访问</h2> <h3 id="spring对jdbc的支持"><a href="#spring对jdbc的支持" class="header-anchor">#</a> Spring对JDBC的支持</h3> <p>JdbcTemplate简介</p> <ul><li>为了使 JDBC 更加易于使用, Spring 在 JDBC API 上定义了一个抽象层, 以此建立一个 JDBC 存取框架</li> <li>作为 Spring JDBC 框架的核心， JDBCTemplate 的设计目的是为不同类型的 JDBC 操作提供模板方法。每个模板方法都能控制整个过程，并允许覆盖过程中的特定任务。通过这种方式，可以在尽可能保留灵活性的情况下，将数据库存取的工作量降到最低。</li></ul> <h3 id="spring-支持哪些-orm-框架"><a href="#spring-支持哪些-orm-框架" class="header-anchor">#</a> Spring 支持哪些 ORM 框架</h3> <p>Hibernate、iBatis、JPA、JDO、OJB</p> <h2 id="六、事务"><a href="#六、事务" class="header-anchor">#</a> 六、事务</h2> <h3 id="spring-中的事务管理"><a href="#spring-中的事务管理" class="header-anchor">#</a> Spring 中的事务管理</h3> <p>作为企业级应用程序框架,，Spring 在不同的事务管理 API 之上定义了一个抽象层，而应用程序开发人员不必了解底层的事务管理 API，就可以使用 Spring 的事务管理机制</p> <p>Spring 既支持<strong>编程式事务管理</strong>，也支持<strong>声明式的事务管理</strong></p> <ul><li>编程式事务管理：将事务管理代码嵌入到业务方法中来控制事务的提交和回滚，在编程式管理事务时，必须在每个事务操作中包含额外的事务管理代码，属于硬编码</li> <li>声明式事务管理：大多数情况下比编程式事务管理更好用。它将事务管理代码从业务方法中分离出来，以声明的方式来实现事务管理。事务管理作为一种横切关注点，可以通过 AOP 方法模块化。Spring 通过 Spring AOP 框架支持声明式事务管理，<strong>声明式事务又分为两种：</strong> <ul><li>基于XML的声明式事务</li> <li>基于注解的声明式事务</li></ul></li></ul> <h3 id="事务管理器"><a href="#事务管理器" class="header-anchor">#</a> 事务管理器</h3> <p>Spring 并不直接管理事务，而是提供了多种事务管理器，他们将事务管理的职责委托给 Hibernate 或者 JTA 等持久化机制所提供的相关平台框架的事务来实现。</p> <p>Spring 事务管理器的接口是 <code>org.springframework.transaction.PlatformTransactionManager</code>，通过这个接口，Spring为各个平台如 JDBC、Hibernate 等都提供了对应的事务管理器，但是具体的实现就是各个平台自己的事情了。</p> <h4 id="spring-中的事务管理器的不同实现"><a href="#spring-中的事务管理器的不同实现" class="header-anchor">#</a> Spring 中的事务管理器的不同实现</h4> <p><strong>事务管理器以普通的 Bean 形式声明在 Spring IOC 容器中</strong></p> <ul><li><p>在应用程序中只需要处理一个数据源, 而且通过 JDBC 存取</p> <div class="language-java extra-class"><pre class="language-java"><code>org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>jdbc<span class="token punctuation">.</span>datasource<span class="token punctuation">.</span><span class="token class-name">DataSourceTransactionManager</span>
</code></pre></div></li> <li><p>在 JavaEE 应用服务器上用 JTA(Java Transaction API) 进行事务管理</p> <div class="language- extra-class"><pre class="language-text"><code>org.springframework.transaction.jta.JtaTransactionManager
</code></pre></div></li> <li><p>用 Hibernate 框架存取数据库</p> <div class="language- extra-class"><pre class="language-text"><code>org.springframework.orm.hibernate3.HibernateTransactionManager
</code></pre></div></li></ul> <p><strong>事务管理器以普通的 Bean 形式声明在 Spring IOC 容器中</strong></p> <h3 id="用事务通知声明式地管理事务"><a href="#用事务通知声明式地管理事务" class="header-anchor">#</a> 用事务通知声明式地管理事务</h3> <ul><li>事务管理是一种横切关注点</li> <li>为了在 Spring 2.x 中启用声明式事务管理，可以通过 tx Schema 中定义的 <a href="tx:advice">tx:advice</a> 元素声明事务通知，为此必须事先将这个 Schema 定义添加到 <beans> 根元素中去</beans></li> <li>声明了事务通知后，就需要将它与切入点关联起来。由于事务通知是在 <a href="aop:config">aop:config</a> 元素外部声明的, 所以它无法直接与切入点产生关联，所以必须在 <a href="aop:config">aop:config</a> 元素中声明一个增强器通知与切入点关联起来.</li> <li>由于 Spring AOP 是基于代理的方法，所以只能增强公共方法。因此, 只有公有方法才能通过 Spring AOP 进行事务管理。</li></ul> <h3 id="用-transactional-注解声明式地管理事务"><a href="#用-transactional-注解声明式地管理事务" class="header-anchor">#</a> 用 @Transactional 注解声明式地管理事务</h3> <ul><li>除了在带有切入点，通知和增强器的 Bean 配置文件中声明事务外，Spring 还允许简单地用 @Transactional 注解来标注事务方法</li> <li>为了将方法定义为支持事务处理的，可以为方法添加 @Transactional 注解，根据 Spring AOP 基于代理机制，<strong>只能标注公有方法.</strong></li> <li>可以在方法或者类级别上添加 @Transactional 注解。当把这个注解应用到类上时， 这个类中的所有公共方法都会被定义成支持事务处理的</li> <li>在 Bean 配置文件中只需要启用 <code>&lt;tx:annotation-driven&gt;</code>元素, 并为之指定事务管理器就可以了</li> <li>如果事务处理器的名称是 transactionManager, 就可以在 <code>&lt;tx:annotation-driven&gt;</code> 元素中省略 <code>transaction-manager</code> 属性，这个元素会自动检测该名称的事务处理器</li></ul> <h3 id="事务传播属性"><a href="#事务传播属性" class="header-anchor">#</a> 事务传播属性</h3> <ul><li>当事务方法被另一个事务方法调用时， 必须指定事务应该如何传播。例如：方法可能继续在现有事务中运行，也可能开启一个新事务，并在自己的事务中运行</li> <li>事务的传播行为可以由传播属性指定，Spring 定义了 7  种类传播行为：</li></ul> <table><thead><tr><th>传播行为</th> <th>意义</th></tr></thead> <tbody><tr><td>PROPAGATION_MANDATORY</td> <td>表示该方法必须运行在一个事务中。如果当前没有事务正在发生，将抛出一个异常</td></tr> <tr><td>PROPAGATION_NESTED</td> <td>表示如果当前正有一个事务在进行中，则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务不存在，行为就像PROPAGATION_REQUIRES一样。</td></tr> <tr><td>PROPAGATION_NEVER</td> <td>表示当前的方法不应该在一个事务中运行。如果一个事务正在进行，则会抛出一个异常。</td></tr> <tr><td>PROPAGATION_NOT_SUPPORTED</td> <td>表示该方法不应该在一个事务中运行。如果一个现有事务正在进行中，它将在该方法的运行期间被挂起。</td></tr> <tr><td>PROPAGATION_SUPPORTS</td> <td>表示当前方法不需要事务性上下文，但是如果有一个事务已经在运行的话，它也可以在这个事务里运行。</td></tr> <tr><td>PROPAGATION_REQUIRES_NEW</td> <td>表示当前方法必须在它自己的事务里运行。一个新的事务将被启动，而且如果有一个现有事务在运行的话，则将在这个方法运行期间被挂起。</td></tr> <tr><td>PROPAGATION_REQUIRES</td> <td>表示当前方法必须在一个事务中运行。如果一个现有事务正在进行中，该方法将在那个事务中运行，否则就要开始一个新事务。</td></tr></tbody></table> <h3 id="spring-支持的事务隔离级别"><a href="#spring-支持的事务隔离级别" class="header-anchor">#</a> Spring 支持的事务隔离级别</h3> <table><thead><tr><th>隔离级别</th> <th>含义</th></tr></thead> <tbody><tr><td>ISOLATION_DEFAULT</td> <td>使用后端数据库默认的隔离级别。</td></tr> <tr><td>ISOLATION_READ_UNCOMMITTED</td> <td>允许读取尚未提交的更改。可能导致脏读、幻影读或不可重复读。</td></tr> <tr><td>ISOLATION_READ_COMMITTED</td> <td>允许从已经提交的并发事务读取。可防止脏读，但幻影读和不可重复读仍可能会发生。</td></tr> <tr><td>ISOLATION_REPEATABLE_READ</td> <td>对相同字段的多次读取的结果是一致的，除非数据被当前事务本身改变。可防止脏读和不可重复读，但幻影读仍可能发生。</td></tr> <tr><td>ISOLATION_SERIALIZABLE</td> <td>完全服从ACID的隔离级别，确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的，因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。</td></tr></tbody></table> <p>事务的隔离级别要得到底层数据库引擎的支持，而不是应用程序或者框架的支持；</p> <p>Oracle 支持的 2 种事务隔离级别，Mysql支持 4 种事务隔离级别。</p> <h3 id="设置隔离事务属性"><a href="#设置隔离事务属性" class="header-anchor">#</a> 设置隔离事务属性</h3> <p>用 @Transactional 注解声明式地管理事务时可以在 @Transactional 的 isolation 属性中设置隔离级别</p> <p>在 Spring 事务通知中, 可以在 <code>&lt;tx:method&gt;</code> 元素中指定隔离级别</p> <h3 id="设置回滚事务属性"><a href="#设置回滚事务属性" class="header-anchor">#</a> 设置回滚事务属性</h3> <ul><li><p>默认情况下只有未检查异常(RuntimeException和Error类型的异常)会导致事务回滚，而受检查异常不会。</p></li> <li><p>事务的回滚规则可以通过 @Transactional 注解的 rollbackFor和 noRollbackFor属性来定义，这两个属性被声明为 Class[] 类型的，因此可以为这两个属性指定多个异常类。</p> <ul><li>rollbackFor：遇到时必须进行回滚</li> <li>noRollbackFor： 一组异常类，遇到时必须不回滚</li></ul></li></ul> <h3 id="超时和只读属性"><a href="#超时和只读属性" class="header-anchor">#</a> 超时和只读属性</h3> <ul><li>由于事务可以在行和表上获得锁， 因此长事务会占用资源, 并对整体性能产生影响</li> <li>如果一个事物只读取数据但不做修改，数据库引擎可以对这个事务进行优化</li> <li>超时事务属性：事务在强制回滚之前可以保持多久，这样可以防止长期运行的事务占用资源</li> <li>只读事务属性：表示这个事务只读取数据但不更新数据，这样可以帮助数据库引擎优化事务</li></ul> <p><strong>设置超时和只读事务属性</strong></p> <ul><li>超时和只读属性可以在 @Transactional 注解中定义，超时属性以秒为单位来计算</li></ul> <p>列出两种方式的示例：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token annotation punctuation">@Transactional</span><span class="token punctuation">(</span>propagation <span class="token operator">=</span> <span class="token class-name">Propagation</span><span class="token punctuation">.</span>NESTED<span class="token punctuation">,</span> timeout <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">,</span> isolation <span class="token operator">=</span> <span class="token class-name">Isolation</span><span class="token punctuation">.</span>READ_COMMITTED<span class="token punctuation">,</span> rollbackFor <span class="token operator">=</span> <span class="token class-name">Exception</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
</code></pre></div><div class="language-xml extra-class"><pre class="language-xml"><code>	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">tx:</span>advice</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>txAdvice<span class="token punctuation">&quot;</span></span> <span class="token attr-name">transaction-manager</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>txManager<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
		<span class="token comment">&lt;!-- the transactional semantics... --&gt;</span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">tx:</span>attributes</span><span class="token punctuation">&gt;</span></span>
			<span class="token comment">&lt;!-- all methods starting with 'get' are read-only --&gt;</span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">tx:</span>method</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>get*<span class="token punctuation">&quot;</span></span> <span class="token attr-name">read-only</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>true<span class="token punctuation">&quot;</span></span> <span class="token attr-name">propagation</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>REQUIRES_NEW<span class="token punctuation">&quot;</span></span> <span class="token attr-name">isolation</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>READ_COMMITTED<span class="token punctuation">&quot;</span></span> <span class="token attr-name">timeout</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>30<span class="token punctuation">&quot;</span></span> <span class="token attr-name">no-rollback-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>java.lang.ArithmeticException<span class="token punctuation">&quot;</span></span><span class="token punctuation">/&gt;</span></span>
			<span class="token comment">&lt;!-- other methods use the default transaction settings (see below) --&gt;</span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">tx:</span>method</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>*<span class="token punctuation">&quot;</span></span><span class="token punctuation">/&gt;</span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">tx:</span>attributes</span><span class="token punctuation">&gt;</span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">tx:</span>advice</span><span class="token punctuation">&gt;</span></span>

    <span class="token comment">&lt;!-- ensure that the above transactional advice runs for any execution
        of an operation defined by the FooService interface --&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">aop:</span>config</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">aop:</span>pointcut</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>fooServiceOperation<span class="token punctuation">&quot;</span></span> <span class="token attr-name">expression</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>execution(* x.y.service.FooService.*(..))<span class="token punctuation">&quot;</span></span><span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">aop:</span>advisor</span> <span class="token attr-name">advice-ref</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>txAdvice<span class="token punctuation">&quot;</span></span> <span class="token attr-name">pointcut-ref</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>fooServiceOperation<span class="token punctuation">&quot;</span></span><span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">aop:</span>config</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><h2 id="七、mvc"><a href="#七、mvc" class="header-anchor">#</a> 七、MVC</h2> <h3 id="spring-mvc-框架有什么用"><a href="#spring-mvc-框架有什么用" class="header-anchor">#</a> Spring MVC 框架有什么用？</h3> <p>Spring Web MVC 框架提供 <strong>模型-视图-控制器</strong> 架构和随时可用的组件，用于开发灵活且松散耦合的 Web 应用程序。 MVC 模式有助于分离应用程序的不同方面，如输入逻辑，业务逻辑和 UI 逻辑，同时在所有这些元素之间提供松散耦合。</p> <h3 id="spring-mvc的优点"><a href="#spring-mvc的优点" class="header-anchor">#</a> Spring MVC的优点</h3> <ul><li>可以支持各种视图技术,而不仅仅局限于JSP</li> <li>与Spring框架集成（如IoC容器、AOP等）</li> <li>清晰的角色分配：前端控制器(dispatcherServlet) ，请求到处理器映射（handlerMapping)，处理器适配器（HandlerAdapter)， 视图解析器（ViewResolver）</li> <li>支持各种请求资源的映射策略</li></ul> <h3 id="spring-mvc-的运行流程-dispatcherservlet描述"><a href="#spring-mvc-的运行流程-dispatcherservlet描述" class="header-anchor">#</a> Spring MVC 的运行流程 | DispatcherServlet描述</h3> <p>在整个 Spring MVC 框架中， DispatcherServlet 处于核心位置，负责协调和组织不同组件以完成请求处理并返回响应的工作</p> <p>SpringMVC 处理请求过程：</p> <ol><li>若一个请求匹配 DispatcherServlet 的请求映射路径(在 web.xml中指定)，WEB 容器将该请求转交给 DispatcherServlet 处理</li> <li>DispatcherServlet 接收到请求后, 将根据请求信息(包括 URL、HTTP方法、请求头、请求参数、Cookie 等)及 HandlerMapping 的配置找到处理请求的处理器(Handler)。可将 HandlerMapping 看成路由控制器， 将 Handler 看成目标主机</li> <li>当 DispatcherServlet 根据 HandlerMapping 得到对应当前请求的 Handler 后，通过 HandlerAdapter 对 Handler 进行封装，再以统一的适配器接口调用 Handler</li> <li>处理器完成业务逻辑的处理后将返回一个 ModelAndView 给 DispatcherServlet，ModelAndView 包含了视图逻辑名和模型数据信息</li> <li>DispatcherServlet 借助 ViewResoler 完成逻辑视图名到真实视图对象的解析</li> <li>得到真实视图对象 View 后, DispatcherServlet 使用这个 View 对ModelAndView 中的模型数据进行视图渲染</li></ol> <p><img src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi8nrg3furj316o0rs3zx.jpg" alt=""></p> <h3 id="spring的controller是单例的吗-多线程情况下controller是线程安全吗"><a href="#spring的controller是单例的吗-多线程情况下controller是线程安全吗" class="header-anchor">#</a> Spring的Controller是单例的吗？多线程情况下Controller是线程安全吗？</h3> <p>controller默认是单例的，不要使用非静态的成员变量，否则会发生数据逻辑混乱。正因为单例所以不是线程安全的</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token annotation punctuation">@Controller</span>
<span class="token comment">//@Scope(&quot;prototype&quot;)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ScopeTestController</span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token keyword">int</span> num <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

    <span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/testScope&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testScope</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token operator">++</span>num<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/testScope2&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testScope2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token operator">++</span>num<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre></div><p>我们首先访问 <code>http://localhost:8080/testScope</code>，得到的答案是<code>1</code>；
然后我们再访问 <code>http://localhost:8080/testScope2</code>，得到的答案是 <code>2</code>。</p> <p>接下来我们再来给<code>controller</code>增加作用多例 <code>@Scope(&quot;prototype&quot;)</code></p> <p>我们依旧首先访问 <code>http://localhost:8080/testScope</code>，得到的答案是<code>1</code>；
然后我们再访问 <code>http://localhost:8080/testScope2</code>，得到的答案还是 <code>1</code>。</p> <p><strong>单例是不安全的，会导致属性重复使用</strong>。</p> <h4 id="解决方案"><a href="#解决方案" class="header-anchor">#</a> 解决方案</h4> <ol><li>不要在controller中定义成员变量</li> <li>万一必须要定义一个非静态成员变量时候，则通过注解@Scope(“prototype”)，将其设置为多例模式。</li> <li>在Controller中使用ThreadLocal变量</li></ol> <h2 id="八、注解"><a href="#八、注解" class="header-anchor">#</a> 八、注解</h2> <h3 id="什么是基于java的spring注解配置-给一些注解的例子"><a href="#什么是基于java的spring注解配置-给一些注解的例子" class="header-anchor">#</a> 什么是基于Java的Spring注解配置? 给一些注解的例子</h3> <p>基于Java的配置，允许你在少量的Java注解的帮助下，进行你的大部分Spring配置而非通过XML文件。</p> <p>以@Configuration 注解为例，它用来标记类可以当做一个bean的定义，被Spring IOC容器使用。</p> <p>另一个例子是@Bean注解，它表示此方法将要返回一个对象，作为一个bean注册进Spring应用上下文。</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token annotation punctuation">@Configuration</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StudentConfig</span> <span class="token punctuation">{</span>
    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">StudentBean</span> <span class="token function">myStudent</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">StudentBean</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="怎样开启注解装配"><a href="#怎样开启注解装配" class="header-anchor">#</a> 怎样开启注解装配？</h3> <p>注解装配在默认情况下是不开启的，为了使用注解装配，我们必须在Spring配置文件中配置 <code>&lt;context:annotation-config/&gt;</code> 元素。</p> <h3 id="spring-mvc-常用注解"><a href="#spring-mvc-常用注解" class="header-anchor">#</a> Spring MVC 常用注解:</h3> <h5 id="controller"><a href="#controller" class="header-anchor">#</a> @Controller</h5> <p>在SpringMVC 中，控制器Controller 负责处理由DispatcherServlet 分发的请求，它把用户请求的数据经过业务处理层处理之后封装成一个Model ，然后再把该Model 返回给对应的View 进行展示。在SpringMVC 中只需使用@Controller 标记一个类是Controller ，然后使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射，这样的Controller 就能被外界访问到。</p> <h5 id="requestmapping"><a href="#requestmapping" class="header-anchor">#</a> @RequestMapping</h5> <p>RequestMapping是一个用来处理请求地址映射的注解，可用于类或方法上</p> <h5 id="requestmapping-2"><a href="#requestmapping-2" class="header-anchor">#</a> @RequestMapping</h5> <p>Spring Framework 4.3 之后引入的基于HTTP方法的变体</p> <ul><li><code>@GetMapping</code></li> <li><code>@PostMapping</code></li> <li><code>@PutMapping</code></li> <li><code>@DeleteMapping</code></li> <li><code>@PatchMapping</code></li></ul> <h5 id="pathvariable"><a href="#pathvariable" class="header-anchor">#</a> @PathVariable</h5> <p>用于将请求URL中的模板变量映射到功能处理方法的参数上，即取出uri模板中的变量作为参数</p> <h5 id="requestparam"><a href="#requestparam" class="header-anchor">#</a> @RequestParam</h5> <p>使用@RequestParam绑定请求参数值，在处理方法入参处使用@RequestParam可以把请求参数传递给请求方法</p> <ul><li>value：参数名</li> <li>required：是否必须。默认为true, 表示请求参数中必须包含对应的参数，若不存在，将抛出异常</li></ul> <h5 id="requestbody"><a href="#requestbody" class="header-anchor">#</a> @RequestBody</h5> <p>@RequestBody 表明方法参数应该绑定到HTTP请求体的值</p> <h5 id="responsebody"><a href="#responsebody" class="header-anchor">#</a> @ResponseBody</h5> <p>@Responsebody 表示该方法的返回结果直接写入HTTP response body中</p> <p>一般在异步获取数据时使用，在使用@RequestMapping后，返回值通常解析为跳转路径，加上@Responsebody后返回结果不会被解析为跳转路径，而是直接写入HTTP response body中。比如异步获取 json 数据，加上@Responsebody后，会直接返回 json 数据。</p> <h5 id="resource和-autowired"><a href="#resource和-autowired" class="header-anchor">#</a> @Resource和@Autowired</h5> <p>@Resource和@Autowired都是做bean的注入时使用，其实@Resource并不是Spring的注解，它的包是javax.annotation.Resource，需要导入，但是Spring支持该注解的注入。</p> <ul><li>共同点：两者都可以写在字段和 setter 方法上。两者如果都写在字段上，那么就不需要再写 setter 方法。</li> <li>不同点
<ul><li>@Autowired 为 Spring 提供的注解，@Autowired 注解是按照类型（byType）装配依赖对象，默认情况下它要求依赖对象必须存在，如果允许 null 值，可以设置它的 required 属性为 false。如果我们想使用按照名称（byName）来装配，可以结合 @Qualifier 注解一起使用</li> <li>@Resource 默认按照 ByName 自动注入，由 J2EE 提供，需要导入包 <code>javax.annotation.Resource</code>。@Resource 有两个重要的属性：name 和 type，而 Spring 将@ Resource 注解的 name 属性解析为bean 的名字，而 type 属性则解析为 bean 的类型。所以，如果使用 name 属性，则使用 byName 的自动注入策略，而使用 type 属性时则使用 byType 自动注入策略。如果既不制定 name 也不制定 type 属性，这时将通过反射机制使用 byName 自动注入策略。</li></ul></li></ul> <h5 id="modelattribute"><a href="#modelattribute" class="header-anchor">#</a> @ModelAttribute</h5> <p>方法入参标注该注解后, 入参的对象就会放到数据模型中</p> <h5 id="sessionattribute"><a href="#sessionattribute" class="header-anchor">#</a> @SessionAttribute</h5> <p>将模型中的某个属性暂存到<strong>HttpSession</strong>中，以便多个请求之间可以共享这个属性</p> <h5 id="cookievalue"><a href="#cookievalue" class="header-anchor">#</a> @CookieValue</h5> <p>@CookieValue可让处理方法入参绑定某个Cookie 值</p> <h5 id="requestheader"><a href="#requestheader" class="header-anchor">#</a> @RequestHeader</h5> <p>请求头包含了若干个属性，服务器可据此获知客户端的信息，通过@RequestHeader即可将请求头中的属性值绑定到处理方法的入参中</p> <h3 id="component-controller-repository-service-有何区别"><a href="#component-controller-repository-service-有何区别" class="header-anchor">#</a> @Component, @Controller, @Repository, @Service 有何区别？</h3> <ul><li>@Component：将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。Spring 的组件扫描机制可以将其拾取并将其拉入应用程序环境中</li> <li>@Controller：将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中</li> <li>@Service：此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。你可以在服务层类中使用 @Service 而不是 @Component，因为它以更好的方式指定了意图</li> <li>@Repository：这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器，并使未经检查的异常有资格转换为 Spring DataAccessException。</li></ul> <h3 id="required-注解有什么作用"><a href="#required-注解有什么作用" class="header-anchor">#</a> @Required 注解有什么作用</h3> <p>这个注解表明bean的属性必须在配置的时候设置，通过一个bean定义的显式的属性值或通过自动装配，若@Required注解的bean属性未被设置，容器将抛出 BeanInitializationException。示例：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Employee</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>
    <span class="token annotation punctuation">@Required</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setName</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">)</span><span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token operator">=</span>name<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">public</span> string <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        <span class="token keyword">return</span> name<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="autowired-注解有什么作用"><a href="#autowired-注解有什么作用" class="header-anchor">#</a> @Autowired 注解有什么作用</h3> <p>@Autowired默认是按照类型装配注入的，默认情况下它要求依赖对象必须存在（可以设置它required属性为false）。@Autowired 注解提供了更细粒度的控制，包括在何处以及如何完成自动装配。它的用法和@Required一样，修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Employee</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>
    <span class="token annotation punctuation">@Autowired</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setName</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token operator">=</span>name<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">public</span> string <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        <span class="token keyword">return</span> name<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="autowired和-resource之间的区别"><a href="#autowired和-resource之间的区别" class="header-anchor">#</a> @Autowired和@Resource之间的区别</h3> <p>用途：做bean的注入时使用</p> <ul><li><p>@Autowired，属于Spring的注解，<code>org.springframework.beans.factory.annotation.Autowired</code></p></li> <li><p>@Resource，不属于Spring的注解，JDK1.6支持的注解，<code>javax.annotation.Resource</code></p></li></ul> <p>共同点：都用来装配bean。写在字段上，或写在setter方法</p> <p>不同点：@Autowired  默认按类型装配。依赖对象必须存在，如果要允许null值，可以设置它的required属性为false  @Autowired(required=false)，也可以使用名称装配，配合@Qualifier注解</p> <p>@Resource默认是按照名称来装配注入的，只有当找不到与名称匹配的bean才会按照类型来装配注入</p> <h3 id="qualifier-注解有什么作用"><a href="#qualifier-注解有什么作用" class="header-anchor">#</a> @Qualifier 注解有什么作用</h3> <p>当创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时，可以使用 @Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。</p> <h3 id="requestmapping-注解有什么用"><a href="#requestmapping-注解有什么用" class="header-anchor">#</a> @RequestMapping 注解有什么用？</h3> <p>@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别：</p> <ul><li>类级别：映射请求的 URL</li> <li>方法级别：映射 URL 以及 HTTP 请求方法</li></ul> <hr> <h2 id="九、其他问题"><a href="#九、其他问题" class="header-anchor">#</a> 九、其他问题</h2> <h3 id="spring-框架中用到了哪些设计模式"><a href="#spring-框架中用到了哪些设计模式" class="header-anchor">#</a> Spring 框架中用到了哪些设计模式？</h3> <ul><li><strong>工厂设计模式</strong> : Spring使用工厂模式通过 <code>BeanFactory</code>、<code>ApplicationContext</code> 创建 bean 对象。</li> <li><strong>代理设计模式</strong> : Spring AOP 功能的实现。</li> <li><strong>单例设计模式</strong> : Spring 中的 Bean 默认都是单例的。</li> <li><strong>模板方法模式</strong> : Spring 中 <code>jdbcTemplate</code>、<code>hibernateTemplate</code> 等以 Template 结尾的对数据库操作的类，它们就使用到了模板模式。</li> <li><strong>包装器设计模式</strong> : 我们的项目需要连接多个数据库，而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。</li> <li><strong>观察者模式:</strong> Spring 事件驱动模型就是观察者模式很经典的一个应用。</li> <li><strong>适配器模式</strong> :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配<code>Controller</code>。</li></ul> <h2 id="参考与来源"><a href="#参考与来源" class="header-anchor">#</a> 参考与来源</h2> <p>https://www.edureka.co/blog/interview-questions/spring-interview-questions/</p> <p>https://crossoverjie.top/2018/07/29/java-senior/ThreadPool/</p></div> <footer class="page-edit" style="display:none;"><!----> <!----></footer> <!----> <!----> <!----></main> <!----></div></div></div></div><div class="global-ui"><div class="back-to-ceiling" style="right:1rem;bottom:6rem;width:2.5rem;height:2.5rem;border-radius:.25rem;line-height:2.5rem;display:none;" data-v-db14854a data-v-db14854a><svg t="1574745035067" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5404" class="icon" data-v-db14854a><path d="M526.60727968 10.90185116a27.675 27.675 0 0 0-29.21455937 0c-131.36607665 82.28402758-218.69155461 228.01873535-218.69155402 394.07834331a462.20625001 462.20625001 0 0 0 5.36959153 69.94390903c1.00431239 6.55289093-0.34802892 13.13561351-3.76865779 18.80351572-32.63518765 54.11355614-51.75690182 118.55860487-51.7569018 187.94566865a371.06718723 371.06718723 0 0 0 11.50484808 91.98906777c6.53300375 25.50556257 41.68394495 28.14064038 52.69160883 4.22606766 17.37162448-37.73630017 42.14135425-72.50938081 72.80769204-103.21549295 2.18761121 3.04276886 4.15646224 6.24463696 6.40373557 9.22774369a1871.4375 1871.4375 0 0 0 140.04691725 5.34970492 1866.36093723 1866.36093723 0 0 0 140.04691723-5.34970492c2.24727335-2.98310674 4.21612437-6.18497483 6.3937923-9.2178004 30.66633723 30.70611158 55.4360664 65.4791928 72.80769147 103.21549355 11.00766384 23.91457269 46.15860503 21.27949489 52.69160879-4.22606768a371.15156223 371.15156223 0 0 0 11.514792-91.99901164c0-69.36717486-19.13165746-133.82216804-51.75690182-187.92578088-3.42062944-5.66790279-4.76302748-12.26056868-3.76865837-18.80351632a462.20625001 462.20625001 0 0 0 5.36959269-69.943909c-0.00994388-166.08943902-87.32547796-311.81420293-218.6915546-394.09823051zM605.93803103 357.87693858a93.93749974 93.93749974 0 1 1-187.89594924 6.1e-7 93.93749974 93.93749974 0 0 1 187.89594924-6.1e-7z" p-id="5405" data-v-db14854a></path><path d="M429.50777625 765.63860547C429.50777625 803.39355007 466.44236686 1000.39046097 512.00932183 1000.39046097c45.56695499 0 82.4922232-197.00623328 82.5015456-234.7518555 0-37.75494459-36.9345906-68.35043303-82.4922232-68.34111062-45.57627738-0.00932239-82.52019037 30.59548842-82.51086798 68.34111062z" p-id="5406" data-v-db14854a></path></svg></div><!----></div></div>
    <script src="/assets/js/app.447d4224.js" defer></script><script src="/assets/js/3.9d76740c.js" defer></script><script src="/assets/js/1.c4fd7d2e.js" defer></script><script src="/assets/js/109.05f775e5.js" defer></script>
  </body>
</html>
